package edu.luc.concordia;


import java.io.File;
import java.io.FileReader;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * Copyright 2008 Project Concordia
 * <p>
 * This file is part of RetailReturnSystem.
 * <p>
 * RetailReturnSystem is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * <p>
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * <p>
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.
 * <hr>
 * Read in the XML initialization file and populates the Inventory with Products and PriceSchemes
 * @author Xingchen Su
 * @version 0.1 2008-10-06
 * @version 0.2 2008-10-09
 */
public class SystemInitializer {
	private ArrayList<String> mySummary; // Maintains a summary of XML parsing process
	private ArrayList<String> myErrors; // Maintains a list of errors during the XML parsing process
	
	/**
	 * Class constructor;Initializes internal variables to empty lists.
	 */
	public SystemInitializer() {
		mySummary = new ArrayList<String>();
		myErrors = new ArrayList<String>();
	}

	/**
	 * Populates the Inventory according to the provided XML file.
	 * I will create two hashmaps at first, 
	 * one for product information and the other for price scheme after xml parsing,
	 * Then I will combine the two hashmaps into Inventory by hashmap traversal
	 * @param input The xml file will be read
	 * @param inven The Inventory class object that will store the information
	 */
	public void initializeInventory(File input, Inventory inven) {
		try {
			XMLReader xr = XMLReaderFactory.createXMLReader();
			SAXCounter saxc = new SAXCounter();
			xr.setContentHandler( saxc );
			xr.parse( new InputSource(
		               new FileReader( input.getPath() )) );
			inven=saxc.combineHashmap();			//put the data into the inventory
			mySummary=saxc.getSummary();			//get summary information
			myErrors=saxc.getErrors();
		}catch ( Exception e )  {
	         e.printStackTrace();
	      }
	}

	/**
	 *  Returns the contents of mySummary
	 */
	public ArrayList<String> listSummary() {
		return mySummary;
	}

	/**
	 * Returns the contents of myErrors
	 */
	public ArrayList<String> listErrors() {
		return myErrors;
	}
	/**
	 * Creat a XML parsing handler
	 * @author Xingchen Su
	 * @version 0.1 2008-10-06
	 * @version 0.2 2008-10-09
	 */
	private class SAXCounter extends DefaultHandler{
		private HashMap<String, ProductInfo> products; // hashmap for product information
		private HashMap<String, String> prices; // hashmap for price scheme
		private Inventory invent;//record temporary inventory
		private String contents; // String to record the content between two tags
		private ProductInfo productInfo;//record temporary product information
		private String psId;//record temporary price scheme id
		private String psrows;//record temporary price scheme
		private ArrayList<String> summary;//temporary summary that will be transfered to mySummory
		private ArrayList<String> errors;//temporary errors that will be transfered to myErrors
		
		/**
		 * Class constructor.
		 */
		public SAXCounter(){
			products = new HashMap<String, ProductInfo>();
			prices = new HashMap<String, String>();
			invent=new Inventory();
			contents = "";
			psId = "";
			psrows="";
			summary=new ArrayList<String>();
			errors=new ArrayList<String>();
		}
		public void startElement(String namespaceURI, String sname, String qname,Attributes atts)
		throws SAXException {
			String ename = sname; // element name
			if ("".equals(ename))
				ename = qname; // namespaceAware = false
			if (ename.equals("product")) { // when the parser encounters <product>,initialize productInfo
				productInfo = new ProductInfo();
			}
			
			if (ename.equals("rows")) { // when the parser encounters <rows>,record PriceSchemeId
				psrows="";
			}
			
		}

		public void endElement(String namespaceURI, String sName,String qName)
		throws SAXException {
			String eName = sName; // element name
			if ("".equals(eName))
				eName = qName; // namespaceAware = false
			if (eName.equals("product")) { // when the parser encounters </product>,add the productInfo to hashmap
				products.put(productInfo.productId, productInfo);
			}
			if (eName.equals("productId")) { // when the parser encounters </productId>,record productId
				productInfo.productId = contents;
			}
			if (eName.equals("productDescription")) { // when the parser encounters </productDescription>, record productDescription
				productInfo.productDescription = contents;
			}
			if (eName.equals("categoryId")) { // when the parser encounters </categoryId>,record categoryId
				productInfo.categoryId = contents;
			}
			if (eName.equals("taxable")) { // when the parser encounters </taxable>,record taxable
				productInfo.taxable = contents;
			}
			if (eName.equals("schemeId")) { // when the parser encounters </schemeId>,record productPriceSchemeId
				productInfo.PriceSchemeIdList.add(contents);
			}
			if (eName.equals("productPriceSchemeId")) { // when the parser encounters </productPriceSchemeId>,record PriceSchemeId
				psId = contents;
			}
			if (eName.equals("row")) { // when the parser encounters <row>,record PriceSchemeId
				if(psrows.equals("")){
					psrows= contents;
				}
				else{
					psrows+=";"+contents;
				}
			}
			if (eName.equals("ProductPriceScheme")) { // when the parser encounters </ProductPriceScheme>,add the PriceScheme to hashmap
				prices.put(psId, psrows);
			}
		}

		public void characters(char[] buf, int offset, int len)
				throws SAXException {
			contents = new String(buf, offset, len).trim();
		}
		/**
		 * Combine products and prices into one hashmap that will be transfered to inventory later.
		 * 
		 */
		public Inventory combineHashmap(){
			Product prod;
			PriceScheme priSch;
			ArrayList<String> list;
			String strmyUID="";
			String strmyDescription="";
			String strmyScheme="";
			//code to combine products and prices
			Iterator<Map.Entry<String,ProductInfo>> it = products.entrySet().iterator(); 
			while(it.hasNext()){
				prod = new Product("","");
				priSch = new PriceScheme("");
				String str = "Added:ID:";
				Map.Entry<String,ProductInfo> mapEntry   =   (Map.Entry<String,ProductInfo>)it.next();  
		        strmyUID = mapEntry.getKey();
		        str+=strmyUID;
		        strmyDescription = mapEntry.getValue().productDescription;
		        if(strmyDescription.equals("")){
					String strE = "Error:ID:"+strmyUID+";no description";
					errors.add(strE);
					continue;
		        }
		        prod=new Product(strmyUID,strmyDescription);
		        str+=";DES:"+strmyDescription+";ProductPriceScheme:";
		        list = mapEntry.getValue().PriceSchemeIdList;

		        int listsize = list.size();
		        if(listsize==0){
		        	String strE = "Error:ID:"+strmyUID+";no ProductPriceScheme";
					errors.add(strE);
					continue;
		        }
		        for(int i=0;i<listsize;i++){
		        	strmyScheme=prices.get(list.get(i));
		        	str+=strmyScheme;
		        	priSch=new PriceScheme(strmyScheme);
		        	prod.addPriceScheme(priSch);
		        }
		        summary.add(str);
		        invent.addProduct(prod);
			}
			return invent;
		}
		/**
		 * Get the summary
		 */
		public ArrayList<String> getSummary(){
			return summary;
		}
		/**
		 * Get the errors
		 */
		public ArrayList<String> getErrors(){
			return errors;
		}

	}
}


